/****************************************************************************
 * Copyright (C) 2025 Chenyz. All rights reserved.
 *
 * @file    TinyOS_Task.c
 *
 * @par     dependencies
 *          - TinyOS_Task.h
 *          - TinyOS_Switch.h
 *          - TinyOS_Config.h
 *          - TinyOS_Lib.h
 *          - TinyOS_Delay.h
 *
 * @author  Chenyz
 *
 * @brief   Task scheduler implementation module for TinyOS with following features:
 *          - Preemptive priority-based task scheduling
 *          - Task context management and switching
 *          - Task delay functionality with system tick support
 *          - Scheduler locking mechanism for critical sections
 *          - Support for multiple priority levels
 *          - Round-robin scheduling within same priority levels
 *          - Task suspension and resumption
 *          - Task deletion with cleanup callbacks
 *          - Task information retrieval
 *          - Cross-platform compatibility (bare-metal/RTOS systems)
 *
 * @version V1.0
 * @date    2025-05-23
 *
 * @note    Coding standard:
 *          - 4 spaces per indent level
 *          - Follow MISRA-C 2012 guidelines
 *          - Compiler compatibility: C99 and above
 *
 ****************************************************************************/

#include "TinyOS_Task.h"
#include "TinyOS_Delay.h"
#include "TinyOS_Hook.h"
/**
 * @brief Initializes a task with specified parameters and prepares its stack
 * 
 * This function sets up a task control block and initializes the task's stack
 * to simulate a context that would be created by a hardware interrupt.
 * 
 * @param pTaskHandler    Pointer to the task control block structure
 * @param pfTaskFunc      Function pointer to the task's entry point
 * @param pParam          Parameter to be passed to the task function
 * @param Priority        Task priority (0 = highest, increasing numbers = lower priority)
 * @param pTaskStack      Pointer to the top of the task's stack area
 */
void TinyOS_TaskInit(
    TinyOS_TaskType* pTaskHandler, 
    void (*pfTaskFunc)(void *),
    void *pParam,
    uint8_t Priority,
    TinyOS_TaskStackType* pTaskStack,
	uint32_t TaskStackSize
)
{
	uint32_t* StackTop;
	pTaskHandler->TaskStackBase = pTaskStack;
	pTaskHandler->TaskStackSize = TaskStackSize;
	memset(pTaskStack, 0x5A5A5A5A, TaskStackSize);
	StackTop = pTaskStack + TaskStackSize / sizeof(TinyOS_TaskStackType);
	
    /* Initialize task stack with simulated interrupt context */
    *(--StackTop) = (uint32_t)(1 << 24);      /* xPSR - Thumb state bit set */
    *(--StackTop) = (uint32_t)pfTaskFunc;     /* PC - Task entry point */
    *(--StackTop) = (uint32_t)(0x14);         /* LR - Return address */
    *(--StackTop) = (uint32_t)(0x12);         /* R12 */
    *(--StackTop) = (uint32_t)(0x3);          /* R3 */
    *(--StackTop) = (uint32_t)(0x2);          /* R2 */
    *(--StackTop) = (uint32_t)(0x1);          /* R1 */
    *(--StackTop) = (uint32_t)(pParam);       /* R0 - Task parameter */

    /* Additional registers */
    *(--StackTop) = (uint32_t)(0x11);         /* R11 */
    *(--StackTop) = (uint32_t)(0x10);         /* R10 */
    *(--StackTop) = (uint32_t)(0x9);          /* R9 */
    *(--StackTop) = (uint32_t)(0x8);          /* R8 */
    *(--StackTop) = (uint32_t)(0x7);          /* R7 */
    *(--StackTop) = (uint32_t)(0x6);          /* R6 */
    *(--StackTop) = (uint32_t)(0x5);          /* R5 */
    *(--StackTop) = (uint32_t)(0x4);          /* R4 */
    
    /* Initialize task control block */
    pTaskHandler->TaskStack = StackTop;
    pTaskHandler->TaskDelayTicks = 0;
    pTaskHandler->TaskPrioity = Priority;
    pTaskHandler->TaskStatus = TINYOS_TASK_READY;
    pTaskHandler->TaskSlice = TINYOS_TASK_SLICE_MAX;
    pTaskHandler->TaskSuspendedCount = 0;
    pTaskHandler->TaskDeleteFlag = 0;
    pTaskHandler->Deleteparam = (void*)0;
    pTaskHandler->TaskDeleteFunc = (void (*)(void*))0;
    
    /* Initialize task list nodes */
    TinyOS_NodeInit(&(pTaskHandler->TaskDelayListNode));
    TinyOS_NodeInit(&(pTaskHandler->TaskListNode));
    
    /* Mark task as ready for scheduling */
    TinyOS_TaskSchedulerReady(pTaskHandler);
	
#if TINYOS_ENABLE_HOOK == 1
	TinyOS_HookTaskInit(pTaskHandler);
#endif
	
}

/**
 * @brief Finds the highest priority task that is ready to run
 * 
 * This function uses the priority bitmap to quickly determine which
 * task should be scheduled next based on priority. For round-robin
 * scheduling within the same priority, it returns the first task
 * in the priority's task list.
 * 
 * @return TinyOS_TaskType* Pointer to the highest priority ready task
 */
TinyOS_TaskType* TinyOS_HighestPriTaskReady(void)
{
    uint32_t HighestPriority = TinyOS_BitmapGetFirstBit(&TaskPriorityBitmap);
    TinyOS_NodeType* SliceFisrtNode = TinyOS_ListFirstNode(&TaskList[HighestPriority]);
    return TINYOS_NODE_PARENT(SliceFisrtNode, TinyOS_TaskType, TaskListNode);
}

/**
 * @brief Initializes the task scheduler
 * 
 * This function prepares the scheduler for operation by initializing
 * the priority bitmap, resetting the scheduler lock count, and
 * initializing all priority task lists.
 */
void TinyOS_TaskSchedulerInit(void)
{
    SchedulerLockCount = 0;
    TinyOS_BitmapInit(&TaskPriorityBitmap);
    
    /* Initialize all priority task lists */
    for(uint8_t i = 0; i < TINYOS_TASK_PRIORITY_COUNT; i++)
    {
        TinyOS_ListInit(&TaskList[i]);
    }
}

/**
 * @brief Locks the task scheduler to prevent context switching
 * 
 * This function increments the scheduler lock count, preventing
 * task switches until a matching number of unlock calls are made.
 * This is useful for protecting critical sections of code.
 */
void TinyOS_TaskSchedulerLockEnable(void)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();
    if(SchedulerLockCount < 255)
    {
        SchedulerLockCount++;
    }
    TinyOS_TaskExitCritical(Stauts);    
}

/**
 * @brief Unlocks the task scheduler, potentially allowing context switching
 * 
 * This function decrements the scheduler lock count. If the count reaches zero,
 * the scheduler is called to potentially switch to a higher priority task.
 */
void TinyOS_TaskSchedulerLockDisable(void)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();
    if(SchedulerLockCount > 0)
    {
        SchedulerLockCount--;
        if(SchedulerLockCount == 0)
        {
            TinyOS_TaskScheduler();
        }
    }
    TinyOS_TaskExitCritical(Stauts);    
}

/**
 * @brief Performs task scheduling and context switching if needed
 * 
 * This function determines if a task switch is needed based on priority
 * and current scheduler lock state. If a higher priority task is ready
 * and the scheduler is not locked, it initiates a context switch.
 */
void TinyOS_TaskScheduler(void)
{
    TinyOS_TaskType* TempTask;
    uint32_t StatusBeforeEnter = TinyOS_TaskEnterCritical();
    
    /* Check if scheduler is locked */
    if(SchedulerLockCount > 0)
    {
        /* Scheduler is locked - do not switch task */
        TinyOS_TaskExitCritical(StatusBeforeEnter);
        return;        
    }
    
    /* Find highest priority ready task */
    TempTask = TinyOS_HighestPriTaskReady();
    
    /* Check if we need to switch tasks */
    if(CurrentTask != TempTask)
    {
        NextTask = TempTask;       
		
#if TINYOS_ENABLE_HOOK == 1
		TinyOS_HookTaskSwitch(CurrentTask, NextTask);
#endif
		
        TinyOS_TaskSwitch();  /* Perform actual context switch */
    }
    
    TinyOS_TaskExitCritical(StatusBeforeEnter);
}
        
/**
 * @brief Marks a task as ready to be scheduled
 * 
 * This function adds a task to its priority's ready list and updates
 * the priority bitmap to indicate that tasks are ready at this priority.
 * 
 * @param Task Pointer to the task to be marked as ready
 */
void TinyOS_TaskSchedulerReady(TinyOS_TaskType* Task)  
{
    TinyOS_ListHeadInsert(&TaskList[Task->TaskPrioity], &(Task->TaskListNode));
    TinyOS_BitmapSet(&TaskPriorityBitmap, Task->TaskPrioity);
}
 
/**
 * @brief Marks a task as not ready to be scheduled
 * 
 * This function removes a task from its priority's ready list and updates
 * the priority bitmap if no more tasks are ready at this priority.
 * 
 * @param Task Pointer to the task to be marked as not ready
 */
void TinyOS_TaskSchedulerUnReady(TinyOS_TaskType* Task)  
{
    TinyOS_ListDeleteNode(&TaskList[Task->TaskPrioity], &(Task->TaskListNode));
    if(TinyOS_ListNodeCount(&TaskList[Task->TaskPrioity]) == 0)
    {
        TinyOS_BitmapClear(&TaskPriorityBitmap, Task->TaskPrioity);
    }
}

/**
 * @brief Suspends a task from execution
 * 
 * This function suspends the specified task, preventing it from being scheduled.
 * If the task is currently running, a context switch will occur.
 * 
 * @param Task Pointer to the task to be suspended
 */
void TinyOS_TaskSuspend(TinyOS_TaskType* Task) 
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();
    
    /* Only suspend if task is not already delayed */
    if(!(Task->TaskStatus & ~TINYOS_TASK_DELAYED))
    {
        Task->TaskSuspendedCount++;
        if(Task->TaskSuspendedCount <= 1)
        {
            Task->TaskStatus |= TINYOS_TASK_SUSPENDED;
            TinyOS_TaskSchedulerUnReady(Task);
            if(Task == CurrentTask)
            {
                TinyOS_TaskScheduler();
            }
        }
    }
    
    TinyOS_TaskExitCritical(Stauts);     
}

/**
 * @brief Resumes a previously suspended task
 * 
 * This function resumes a task that was previously suspended, making it
 * eligible for scheduling again.
 * 
 * @param Task Pointer to the task to be resumed
 */
void TinyOS_TaskResume(TinyOS_TaskType* Task) 
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();
    
    if(Task->TaskStatus & TINYOS_TASK_SUSPENDED)
    {
        Task->TaskSuspendedCount--;
        if(Task->TaskSuspendedCount == 0)
        {
            Task->TaskStatus &= ~TINYOS_TASK_SUSPENDED;
            TinyOS_TaskSchedulerReady(Task);
            TinyOS_TaskScheduler();
        }
    }
    
    TinyOS_TaskExitCritical(Stauts);   
}

/**
 * @brief Removes a task from the scheduler without cleanup
 * 
 * This function removes a task from the scheduler's ready list but
 * does not perform any cleanup operations. Used internally by other
 * task deletion functions.
 * 
 * @param Task Pointer to the task to be removed from scheduling
 */
void TinyOS_TaskSchedulerDelete(TinyOS_TaskType* Task)  
{
    TinyOS_ListDeleteNode(&TaskList[Task->TaskPrioity], &(Task->TaskListNode));
    if(TinyOS_ListNodeCount(&TaskList[Task->TaskPrioity]) == 0)
    {
        TinyOS_BitmapClear(&TaskPriorityBitmap, Task->TaskPrioity);
    }
}

/**
 * @brief Sets a callback function to be called when a task is deleted
 * 
 * This function registers a callback function that will be called when
 * the specified task is deleted, allowing for custom cleanup operations.
 * 
 * @param Task Pointer to the task
 * @param DeleteFunc Callback function to be called on task deletion
 * @param DeleteParam Parameter to be passed to the callback function
 */
void TinyOS_TaskSetDeleteCallFunc(
    TinyOS_TaskType* Task,
    void(*DeleteFunc)(void * DeleteParam),
    void* DeleteParam
)
{
    Task->TaskDeleteFunc = DeleteFunc;
    Task->Deleteparam = DeleteParam;
}    

/**
 * @brief Forces immediate deletion of a task
 * 
 * This function immediately deletes the specified task, performing
 * necessary cleanup and potentially triggering a context switch.
 * 
 * @param Task Pointer to the task to be deleted
 */
void TinyOS_TaskForceDelete(TinyOS_TaskType* Task)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();     

    /* Handle different task states */
    if(Task->TaskStatus & TINYOS_TASK_DELAYED)
    {
        TinyOS_TaskDelayListDelete(Task);
    }
    else if(!(Task->TaskStatus & TINYOS_TASK_SUSPENDED))
    {
        TinyOS_TaskSchedulerDelete(Task);       
    }
    
    /* Call delete callback if registered */
    if(Task->TaskDeleteFunc)
    {
        Task->TaskDeleteFunc(Task->Deleteparam);
    }
    
    /* Schedule another task if deleting current task */
    if(CurrentTask == Task)
    {
        TinyOS_TaskScheduler();
    }
   
    TinyOS_TaskExitCritical(Stauts);      
}

/**
 * @brief Requests deletion of a task
 * 
 * This function sets a flag indicating that the specified task should
 * be deleted. The actual deletion should be handled by the task itself
 * or by a cleanup mechanism.
 * 
 * @param Task Pointer to the task to be marked for deletion
 */
void TinyOS_TaskDeleteRequest(TinyOS_TaskType* Task)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();   
    
    Task->TaskDeleteFlag = 1;
    
    TinyOS_TaskExitCritical(Stauts); 
}

/**
 * @brief Checks if the current task has been requested to be deleted
 * 
 * This function checks if a deletion request has been made for the
 * currently running task.
 * 
 * @return uint8_t 1 if deletion requested, 0 otherwise
 */
uint8_t TinyOS_TaskIsRequestedDelete(void)
{
    uint8_t IsRequestedDelFlag;
    uint32_t Stauts = TinyOS_TaskEnterCritical();   
    
    IsRequestedDelFlag = CurrentTask->TaskDeleteFlag;
    
    TinyOS_TaskExitCritical(Stauts);       
    return IsRequestedDelFlag;
}

/**
 * @brief Deletes the currently running task
 * 
 * This function deletes the currently executing task, performing
 * necessary cleanup and triggering a context switch to another task.
 */
void TinyOS_TaskDeleteSelf(void)
{   
    uint32_t Stauts = TinyOS_TaskEnterCritical();   
    
    TinyOS_TaskSchedulerDelete(CurrentTask);
    if(CurrentTask->TaskDeleteFunc)
    {
        CurrentTask->TaskDeleteFunc(CurrentTask->Deleteparam);
    }    
    TinyOS_TaskScheduler();
    
    TinyOS_TaskExitCritical(Stauts);     
}

/**
 * @brief Retrieves information about a task
 * 
 * This function retrieves various information about the specified task,
 * including its delay ticks, priority, time slice, status, and suspension count.
 * 
 * @param Task Pointer to the task to get information about
 * @param TaskInfo Pointer to a structure where the information will be stored
 */
void TinyOS_TaskGetInfo(TinyOS_TaskType* Task, TinyOS_TaskInfo* TaskInfo)
{
	uint32_t* pStackEnd;
    uint32_t Stauts = TinyOS_TaskEnterCritical();   
    
    TaskInfo->TaskInfoDelayTicks = Task->TaskDelayTicks;
    TaskInfo->TaskInfoPrioity = Task->TaskPrioity;
    TaskInfo->TaskInfoSlice = Task->TaskSlice;
    TaskInfo->TaskInfoStatus = Task->TaskStatus;
    TaskInfo->TaskInfoSuspendedCount = Task->TaskSuspendedCount;
	TaskInfo->TaskStackSize = Task->TaskStackSize;
	TaskInfo->TaskStackSizeFree = 0;
	
	pStackEnd = Task->TaskStackBase;
	while((*pStackEnd++ == 0x5A5A5A5A) && 
		(pStackEnd <= Task->TaskStackBase + Task->TaskStackSize / sizeof(TinyOS_TaskStackType))
	)
	{
		TaskInfo->TaskStackSizeFree++;		
	}
	TaskInfo->TaskStackSizeFree *= sizeof(TinyOS_TaskStackType);
	
    TinyOS_TaskExitCritical(Stauts);      
}

